#include <fstream>
#include <algorithm>
#include <QtOpenGL>
#include "glext.h"
#include "ship.h"

Ship::Ship(const QGLContext* context)
{
	if(0 == glActiveTextureARB)
		glActiveTextureARB  = (void (APIENTRY *) (GLenum)) context->getProcAddress("glActiveTextureARB");
}

bool Ship::loadViewModel( const std::string& fileName )
{
	viewModel.meshes.clear();
	viewModel.materials.clear();
	viewModel.vertices.clear();

	std::ifstream file(fileName.c_str(), std::ios_base::out | std::ios_base::binary);

	if( !file.is_open() )
        return false;

	std::string string;
	std::getline(file, string, '\0');
	
	if( string != "Visu" )
		return false;

	unsigned int triNum = 0;
	file.read((char*)&triNum, sizeof(int));
	viewModel.vertices.resize(triNum);
	file.read((char*)&viewModel.vertices[0], sizeof(Point)*triNum);

	unsigned short matNum = 0;
	file.read((char*)&matNum, sizeof(short));
	viewModel.materials.resize(matNum);
	for(int i = 0; i != matNum; ++i)
	{
		file.read((char*)&viewModel.materials[i], sizeof(float) * 11);
		std::getline(file, string, '\0');
		std::string texDirName;
		std::copy(fileName.begin(), std::find(fileName.rbegin(), fileName.rend(), '/').base(), std::back_inserter<std::string>(texDirName));
		viewModel.materials[i].texFileName = texDirName + string;
		std::getline(file, string, '\0');
		viewModel.materials[i].bumpFileName = texDirName + string;
	}

	unsigned int meshNum = 0;
	file.read((char*)&meshNum, sizeof(int));
	viewModel.meshes.resize(meshNum);
	for(int i = 0; i != meshNum; ++i)
	{
		file.read((char*)&viewModel.meshes[i].matIndex, sizeof(short));
		unsigned int triNum = 0;
		file.read((char*)&triNum, sizeof(int));
		viewModel.meshes[i].triangles.resize(triNum);
		file.read((char*)&viewModel.meshes[i].triangles[0], sizeof(Triangle) * triNum);
	}

	file.close();

	return true;
}

bool Ship::loadTextures( QGLWidget* glwidget )
{
	if(!glwidget)
		return false;

	viewModel.textures.clear();
	viewModel.bumpTextures.clear();

	for(int i = 0; i != viewModel.materials.size(); ++i)
	{
		viewModel.textures.push_back( glwidget->bindTexture(QImage(viewModel.materials[i].texFileName.c_str())) );
		viewModel.bumpTextures.push_back( glwidget->bindTexture(QImage(viewModel.materials[i].bumpFileName.c_str())) );
	}

	return true;
}

bool Ship::loadShaders()
{
	bool result = shader.addShaderFromSourceFile(QGLShader::Vertex, "./shaders/ship.vert");
	result &= shader.addShaderFromSourceFile(QGLShader::Fragment, "./shaders/ship.frag");
	result &= shader.link();

	return result;
}

void Ship::draw(const ShipRenderData& mathModel, const GLenum shadowTexNum)
{
	if(0 == glActiveTextureARB)
		return;

	shader.bind();

	if(shadowTexNum >= GL_TEXTURE0_ARB)
	{
		unsigned short num = shadowTexNum - GL_TEXTURE0_ARB;
		shader.setUniformValue("shadowMap", num);
		shader.setUniformValue("shadowMapNum", num);
		shader.setUniformValue("shadowOn", 1);
	}

	glPushMatrix();

	glTranslatef(0,mathModel.heaving,0);
	glRotatef(-90+mathModel.course, 0, -1, 0);
	glRotatef(-mathModel.rollAngle,0,0,1);

	for(int i = 0; i != viewModel.meshes.size(); ++i)
	{
		float diffuse[4] = {viewModel.materials[viewModel.meshes[i].matIndex].diffuse[0],
						viewModel.materials[viewModel.meshes[i].matIndex].diffuse[1],
						viewModel.materials[viewModel.meshes[i].matIndex].diffuse[2],
						viewModel.materials[viewModel.meshes[i].matIndex].transparency};

		glMaterialfv( GL_FRONT, GL_AMBIENT, viewModel.materials[viewModel.meshes[i].matIndex].ambient );
		glMaterialfv( GL_FRONT, GL_DIFFUSE, diffuse );
		glMaterialfv( GL_FRONT, GL_SPECULAR, viewModel.materials[viewModel.meshes[i].matIndex].specular );
		glMaterialf( GL_FRONT, GL_SHININESS, viewModel.materials[viewModel.meshes[i].matIndex].shiness );

		glActiveTextureARB(GL_TEXTURE0_ARB);

		if( viewModel.textures[viewModel.meshes[i].matIndex] > 0 )
		{
			glBindTexture( GL_TEXTURE_2D, viewModel.textures[viewModel.meshes[i].matIndex] );
			shader.setUniformValue("texOn", 1);
		}
		else
		{
			glBindTexture( GL_TEXTURE_2D, 0 );
			shader.setUniformValue("texOn", 0);
		}

		shader.setUniformValue("colorMap", 0);

		glActiveTextureARB(GL_TEXTURE1_ARB);

		if( viewModel.bumpTextures[viewModel.meshes[i].matIndex] > 0 )
		{
			glBindTexture( GL_TEXTURE_2D, viewModel.bumpTextures[viewModel.meshes[i].matIndex] );
			shader.setUniformValue("bumpOn", 1);
		}
		else
		{
			glBindTexture( GL_TEXTURE_2D, 0 );
			shader.setUniformValue("bumpOn", 0);
		}

		shader.setUniformValue("normalMap", 1);



		if( qAbs(viewModel.materials[viewModel.meshes[i].matIndex].transparency - 1.0f) > 1e-2 )
			glEnable(GL_BLEND);
		else
			glDisable(GL_BLEND);

		int tangentLoc = shader.attributeLocation("tangent");

		glBegin( GL_TRIANGLES );
		for(int j = 0; j != viewModel.meshes[i].triangles.size(); ++j)
		{
			shader.setAttributeValue(tangentLoc, viewModel.meshes[i].triangles[j].tangent.coords[0],
													viewModel.meshes[i].triangles[j].tangent.coords[1],
													viewModel.meshes[i].triangles[j].tangent.coords[2]);

			for( int k = 0; k != 3; ++k)
			{
				glNormal3fv(viewModel.meshes[i].triangles[j].normals[k].coords);
				glTexCoord2f(viewModel.meshes[i].triangles[j].texCoord[k].coords[0], viewModel.meshes[i].triangles[j].texCoord[k].coords[1]);
				glVertex3fv(viewModel.vertices[ viewModel.meshes[i].triangles[j].vertexIndices[k] ].coords);
			}
		}
		glEnd();
	}

	glPopMatrix();

	shader.release();
}